package com.zxj.databaseframework.db;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.zxj.databaseframework.annotation.DbField;
import com.zxj.databaseframework.annotation.DbTable;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by zuo on 2018/3/8.
 */

public class BaseDao<T> implements IBaseDao<T> {
    /**
     * 持有数据库操作类的引用
     */
    private SQLiteDatabase database;
    /**
     * 表名
     */
    private String tableName;
    /**
     * 持有操作数据库表所对应的java类型
     */
    private Class<T> entityClass;
    /**
     * 维护这表名与成员变量名的映射关系
     * key---》字段名
     * value --》成员变量
     */
    private HashMap<String, Field> cacheMap;
    /**
     * 保证实例化一次
     */
    private boolean isInit = false;

    protected synchronized boolean init(SQLiteDatabase database, Class<T> entityClass) {
        this.database = database;
        this.entityClass = entityClass;
        //可以根据传入的entityClass类型来建立表,只需要建一次
        if (!isInit) {
            //取到表名
            DbTable dbTable = entityClass.getAnnotation(DbTable.class);
            if (null == dbTable) {
                //反射到类名
                tableName = entityClass.getSimpleName();
            } else {
                //取注解上的名字
                tableName = dbTable.value();
            }
            if (!database.isOpen()) {
                return false;
            }

            String createTableSql = createTable();
            database.execSQL(createTableSql);
            cacheMap = new HashMap<>();
            initCacheMap();
            isInit = true;
        }
        return false;
    }

    /**
     * 维护数据库的字段名与成员变量名的映射关系
     * key---》字段名
     * value --》成员变量
     */
    private void initCacheMap() {
        //1.取到所有的列名
        String sql = "select * from " + tableName + " limit 1,0";//空表
        Cursor cursor = null;
        try {
            cursor = database.rawQuery(sql, null);
            String[] columnNames = cursor.getColumnNames();
            //2.取所有的成员变量
            Field[] columnFields = entityClass.getDeclaredFields();
            //把所有字段的访问权限打开
            for (Field columnField : columnFields) {
                columnField.setAccessible(true);
            }
            //对1和2进行映射
            for (String columnName : columnNames) {
                Field columnField = null;
                for (Field field : columnFields) {
                    String fieldName;
                    DbField dbField = field.getAnnotation(DbField.class);
                    if (dbField != null) {
                        fieldName = dbField.value();
                    } else {
                        fieldName = field.getName();
                    }
                    if (columnName.equals(fieldName)) {
                        columnField = field;
                        break;
                    }
                }
                if (columnField != null) {
                    cacheMap.put(columnName, columnField);
                }
            }
        } catch (Exception e) {
        } finally {
            if (cursor != null)
                cursor.close();
        }
    }

    public String createTable() {
        StringBuffer sb = new StringBuffer();
        sb.append("create table if not exists ")
                .append(tableName + "(id INTEGER PRIMARY KEY AUTOINCREMENT, ");
        Field[] fields = this.entityClass.getDeclaredFields();
        for (Field field : fields) {
            Class<?> type = field.getType();
            DbField dbField = field.getAnnotation(DbField.class);
            String typeTemp = null;
            if (null == dbField) {//字段上面没有注解
                if (type == String.class) {
                    typeTemp = " TEXT,";
                } else if (type == int.class || type == Integer.class) {
                    typeTemp = " INTEGER,";
                } else if (type == long.class) {
                    typeTemp = " BIGINT,";
                } else if (type == Double.class || type == double.class) {
                    typeTemp = " DOUBLE,";
                } else if (type == byte[].class) {
                    typeTemp = " BLOB,";
                } else {
                    //不支持的类型号
                    continue;
                }
                sb.append(field.getName() + typeTemp);
            } else {
                if (type == String.class) {
                    typeTemp = " TEXT,";
                } else if (type == int.class || type == Integer.class) {
                    typeTemp = " INTEGER,";
                } else if (type == long.class) {
                    typeTemp = " BIGINT,";
                } else if (type == Double.class || type == double.class) {
                    typeTemp = " DOUBLE,";
                } else if (type == byte[].class) {
                    typeTemp = " BLOB,";
                } else {
                    //不支持的类型号
                    continue;
                }
                sb.append(dbField.value() + typeTemp);
            }
        }
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append(");");
        Log.e("zxj", "sql=" + sb.toString());
        return sb.toString();
    }


    @Override
    public long insert(T entity) {
        //准备好ContentValues中需要的数据
        Map<String, String> map = getValues(entity);
        //把数据转移到ContentValues中
        ContentValues values = getContentValues(map);
        //开始插入
        long result = database.insert(tableName, null, values);
        return result;
    }

    @Override
    public int update(T entity, T where) {
//        database.update(tableName,contentValues,"1=1 and name = ? and password =?",new String[]{""});
        Map<String, String> map = getValues(entity);
        Map<String, String> whereClause = getValues(where);
        ContentValues contentValues = getContentValues(map);
        Condition condition = new Condition(whereClause);
        int result = database.update(tableName, contentValues, condition.whereClause, condition.whereArgs);
        return result;
    }

    @Override
    public int delete(T where) {
        Map<String, String> whereClause = getValues(where);
        Condition condition = new Condition(whereClause);
        int result = database.delete(tableName, condition.whereClause, condition.whereArgs);
        return result;
    }

    @Override
    public List<T> query(T where) {
        return query(where, null, null, null);
    }

    @Override
    public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
//        Cursor query = database.query(tableName, null, "id=?", new String[]{""}, null, null, orderBy, limit);
        Map<String, String> whereClause = getValues(where);
        Condition condition = new Condition(whereClause);
        String limitStr = null;
        if (startIndex != null && limit != null) {
            limitStr = startIndex + "," + limit;
        }

        Cursor cursor = database.query(tableName, null, condition.whereClause, condition.whereArgs,
                null, null, orderBy, limitStr);
        List<T> result = getResult(cursor, where);
        return result;
    }

    private List<T> getResult(Cursor cursor, T where) {
        ArrayList list = new ArrayList();
        Object item = null;

        try {
            while (cursor.moveToNext()) {
                item = where.getClass().newInstance();
                Set<String> keySet = cacheMap.keySet();
                Iterator<String> it = keySet.iterator();
                while (it.hasNext()){
                    //得到列名
                    String columnName = it.next();
                    //然后以列名拿到列名在游标的位子
                    int columnIndex = cursor.getColumnIndex(columnName);
                    Field field = cacheMap.get(columnName);
                    Class<?> type = field.getType();
                    if (columnIndex != -1) {
                        if (type == String.class) {
                            field.set(item, cursor.getString(columnIndex));
                        } else if (type == Integer.class || type == int.class) {
                            field.set(item, cursor.getInt(columnIndex));
                        } else if (type == Long.class) {
                            field.set(item, cursor.getLong(columnIndex));
                        } else if (type == Double.class || type == double.class) {
                            field.set(item, cursor.getDouble(columnIndex));
                        } else if (type == Float.class || type == float.class) {
                            field.set(item, cursor.getFloat(columnIndex));
                        } else if (type == byte[].class) {
                            field.set(item, cursor.getBlob(columnIndex));
                        } else {
                            //不支持的类型
                            continue;
                        }
                    }
                }
                list.add(item);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return list;
    }

    @Override
    public List<T> query(String sql,Class<T> selectionArgs) {
        List<T> result = null;
        try {
            T t = selectionArgs.newInstance();
            Map<String, String> whereClause = getValues(t);
            Condition condition = new Condition(whereClause);
            Cursor cursor = database.rawQuery(sql, condition.whereArgs);
            result = getResult(cursor, t);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }

    private class Condition {
        private String whereClause;
        private String[] whereArgs;

        public Condition(Map<String, String> whereClause) {
            StringBuilder sb = new StringBuilder();
            ArrayList list = new ArrayList();//whereArgs里面的内容存入list
            sb.append(" 1=1");
            Set<String> keys = whereClause.keySet();
            Iterator<String> it = keys.iterator();
            while (it.hasNext()) {
                String key = it.next();
                String value = whereClause.get(key);
                sb.append(" and " + key + " =? ");
                list.add(value);
            }
            this.whereClause = sb.toString();
            this.whereArgs = (String[]) list.toArray(new String[list.size()]);
        }
    }

    private ContentValues getContentValues(Map<String, String> map) {
        ContentValues contentValues = new ContentValues();
        Set keys = map.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            String value = map.get(key);
            if (value != null) {
                contentValues.put(key, value);
            }
        }
        return contentValues;
    }

    /**
     * 从cacheMap获取数据库中字段和值的对应Map
     * key-->字段名
     * value-->值
     *
     * @param entity
     * @return
     */
    private Map<String, String> getValues(T entity) {
        HashMap<String, String> result = new HashMap<>();
        Iterator<Field> it = cacheMap.values().iterator();
        while (it.hasNext()) {
            Field field = it.next();
            //获取列名
            String cacheKey = null;
            String cacheValue = null;
            DbField dbField = field.getAnnotation(DbField.class);
            if (dbField != null) {
                cacheKey = dbField.value();
            } else {
                cacheKey = field.getName();
            }
            try {
                if (null == field.get(entity)) {
                    continue;
                }
                cacheValue = field.get(entity).toString();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            result.put(cacheKey, cacheValue);
        }
        return result;
    }
}
